/* -*- mode: c++ -*- */
/****************************************************************************
 *****                                                                  *****
 *****                   Classification: UNCLASSIFIED                   *****
 *****                    Classified By:                                *****
 *****                    Declassify On:                                *****
 *****                                                                  *****
 ****************************************************************************
 *
 *
 * Developed by: Naval Research Laboratory, Tactical Electronic Warfare Div.
 *               EW Modeling & Simulation, Code 5773
 *               4555 Overlook Ave.
 *               Washington, D.C. 20375-5339
 *
 * License for source code can be found at:
 * https://github.com/USNavalResearchLaboratory/simdissdk/blob/master/LICENSE.txt
 *
 * The U.S. Government retains all rights to use, duplicate, distribute,
 * disclose, or release this software.
 *
 */
#include "osg/PolygonStipple"
#include "osg/StateSet"
#include "osgEarth/Capabilities"
#include "osgEarth/Registry"
#include "osgEarth/VirtualProgram"
#include "simVis/Shaders.h"
#include "simVis/PolygonStipple.h"

namespace simVis {

namespace
{
const std::string USE_POLYGON_STIPPLE_DEFINE = "SIMVIS_USE_POLYGON_STIPPLE";
const std::string STIPPLE_INDEX_UNIFORM = "simvis_stippleindex";

/// Stippling mask for gates/beams
const GLubyte PATTERN_MASK_0[] =
{
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99,
  0x44, 0x44, 0x44, 0x44, 0x99, 0x99, 0x99, 0x99
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_1[] =
{
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66,
  0x44, 0x44, 0x44, 0x44, 0x66, 0x66, 0x66, 0x66
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_2[] =
{
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33,
  0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_3[] =
{
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99,
  0xAA, 0xAA, 0xAA, 0xAA, 0x99, 0x99, 0x99, 0x99
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_4[] =
{
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66,
  0xAA, 0xAA, 0xAA, 0xAA, 0x66, 0x66, 0x66, 0x66
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_5[] =
{
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33,
  0xAA, 0xAA, 0xAA, 0xAA, 0x33, 0x33, 0x33, 0x33
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_6[] =
{
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99,
  0xDD, 0xDD, 0xDD, 0xDD, 0x99, 0x99, 0x99, 0x99
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_7[] =
{
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66,
  0xDD, 0xDD, 0xDD, 0xDD, 0x66, 0x66, 0x66, 0x66
};

/// A stippling mask for gates/beams
const GLubyte PATTERN_MASK_8[] =
{
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33,
  0xDD, 0xDD, 0xDD, 0xDD, 0x33, 0x33, 0x33, 0x33
};
}

PolygonStipple::PolygonStipple()
{
}

PolygonStipple::~PolygonStipple()
{
}

void PolygonStipple::installShaderProgram(osg::StateSet* intoStateSet)
{
  // Shader side: Install the shader.  FFP: do nothing
  if (osgEarth::Registry::capabilities().supportsGLSL(3.3f))
  {
    osgEarth::VirtualProgram* vp = osgEarth::VirtualProgram::getOrCreate(intoStateSet);
    simVis::Shaders shaders;
    shaders.load(vp, shaders.polygonStippleFragment());
    intoStateSet->getOrCreateUniform(STIPPLE_INDEX_UNIFORM, osg::Uniform::UNSIGNED_INT)->set(0u);
  }
}

void PolygonStipple::setFfpStipplePattern_(osg::StateSet* stateSet, unsigned int patternIndex)
{
  if (!stateSet)
    return;

  const GLubyte* stippleArray = PATTERN_MASK_0;
  switch (patternIndex)
  {
  case 1:
    stippleArray = PATTERN_MASK_1;
    break;
  case 2:
    stippleArray = PATTERN_MASK_2;
    break;
  case 3:
    stippleArray = PATTERN_MASK_3;
    break;
  case 4:
    stippleArray = PATTERN_MASK_4;
    break;
  case 5:
    stippleArray = PATTERN_MASK_5;
    break;
  case 6:
    stippleArray = PATTERN_MASK_6;
    break;
  case 7:
    stippleArray = PATTERN_MASK_7;
    break;
  case 8:
    stippleArray = PATTERN_MASK_8;
    break;
  default:
    break;
  }
  // Set the state attribute
  stateSet->setAttributeAndModes(new osg::PolygonStipple(stippleArray), osg::StateAttribute::ON);
}

void PolygonStipple::setValues(osg::StateSet* stateset, bool enabled, unsigned int patternIndex)
{
  if (stateset == NULL)
    return;

  // Limit the pattern index
  if (patternIndex > 8u)
    patternIndex = 0u;

  // Need GLSL 3.3 to use polygon stipple, else fall back to FFP and hope for compatibility mode
  const bool useShader = osgEarth::Registry::capabilities().supportsGLSL(3.3f);
  if (useShader)
  {
    // GL 3.3 implementation uses a shader
    if (enabled)
    {
      // Set the define and update the pattern uniform
      stateset->setDefine(USE_POLYGON_STIPPLE_DEFINE);
      stateset->getOrCreateUniform(STIPPLE_INDEX_UNIFORM, osg::Uniform::UNSIGNED_INT)->set(patternIndex);
    }
    else
    {
      stateset->removeDefine(USE_POLYGON_STIPPLE_DEFINE);
    }
  }
  else
  {
    // Fixed function pipeline; controlled by osg::StateAttribute
    if (enabled)
      PolygonStipple::setFfpStipplePattern_(stateset, patternIndex);
    else
      stateset->removeAttribute(osg::StateAttribute::POLYGONSTIPPLE);
  }
}

}
